/*
* Copyright 2011 Kestrel Signal Processing, Inc.
* Copyright 2011 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <stdio.h>
#include <string.h>
#include "jwsmtp.h"
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "sqlite3.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <Configuration.h>
#include "auto_add_user.h"

extern ConfigurationTable gConfig;
const int SubscriberRegistry_db::CALLID_BASE = 2000;

using namespace std;

SubscriberRegistry_db::SubscriberRegistry_db()
{
	string ldb = gConfig.getStr("SubscriberRegistry.db");
	int rc = sqlite3_open(ldb.c_str(),&mDB);
	if (rc) {
		LOG(EMERG) << "Cannot open SubscriberRegistry database: " << sqlite3_errmsg(mDB);
		sqlite3_close(mDB);
		mDB = NULL;
		return;
	}
}



SubscriberRegistry_db::~SubscriberRegistry_db()
{
	if (mDB) sqlite3_close(mDB);
}




SubscriberRegistry_db::Status SubscriberRegistry_db::sqlLocal(const char *query, char **resultptr)
{
	LOG(INFO) << query;

	if (!resultptr) {
		if (!sqlite3_command(db(), query)) return FAILURE;
		return SUCCESS;
	}

	sqlite3_stmt *stmt;
	if (sqlite3_prepare_statement(db(), &stmt, query)) {
		LOG(ERR) << "sqlite3_prepare_statement problem";
		return FAILURE;
	}
	int src = sqlite3_run_query(db(), stmt);
	if (src != SQLITE_ROW) {
		sqlite3_finalize(stmt);
		return FAILURE;
	}
	char *column = (char*)sqlite3_column_text(stmt, 0);
	if (!column) {
		LOG(ERR) << "Subscriber registry returned a NULL column.";
		sqlite3_finalize(stmt);
		return FAILURE;
	}
	*resultptr = strdup(column);
	sqlite3_finalize(stmt);
	return SUCCESS;
}



char *SubscriberRegistry_db::sqlQuery(const char *unknownColumn, const char *table, const char *knownColumn, const char *knownValue)
{
	char *result = NULL;
	SubscriberRegistry_db::Status st;
	ostringstream os;
	os << "select " << unknownColumn << " from " << table << " where " << knownColumn << " = \"" << knownValue << "\"";
	// try to find locally
	st = sqlLocal(os.str().c_str(), &result);
	if (st == SUCCESS) {
		// got it.  return it.
		LOG(INFO) << "result = " << result;
		return result;
	}
	LOG(INFO) << "not found";
	return NULL;
}

char *SubscriberRegistry_db::sqlQuery_count(void)
{
	char *result = NULL;
	SubscriberRegistry_db::Status st;
	ostringstream os;
	os << "select count(*) from  sip_buddies ";
	// try to find locally
	st = sqlLocal(os.str().c_str(), &result);
	if (st == SUCCESS) {
		// got it.  return it.
		LOG(INFO) << "result = " << result;
		return result;
	}
	LOG(INFO) << "not found";
	return NULL;
}

SubscriberRegistry_db::Status SubscriberRegistry_db::sqlUpdate(const char *stmt)
{
	LOG(INFO) << stmt;
	SubscriberRegistry_db::Status st = sqlLocal(stmt, NULL);
	return st;
}



char *SubscriberRegistry_db::getIMSI(const char *ISDN)
{
	if (!ISDN) {
		LOG(WARNING) << "SubscriberRegistry::getIMSI attempting lookup of NULL ISDN";
		return NULL;
	}
	LOG(INFO) << "getIMSI(" << ISDN << ")";
	return sqlQuery("dial", "dialdata_table", "exten", ISDN);
}



char *SubscriberRegistry_db::getCLIDLocal(const char* IMSI)
{
	if (!IMSI) {
		LOG(WARNING) << "SubscriberRegistry::getCLIDLocal attempting lookup of NULL IMSI";
		return NULL;
	}
	LOG(INFO) << "getCLIDLocal(" << IMSI << ")";
	return sqlQuery("callerid", "sip_buddies", "name", IMSI);
}



char *SubscriberRegistry_db::getCLIDGlobal(const char* IMSI)
{
	if (!IMSI) {
		LOG(WARNING) << "SubscriberRegistry::getCLIDGlobal attempting lookup of NULL IMSI";
		return NULL;
	}
	LOG(INFO) << "getCLIDGlobal(" << IMSI << ")";
	return sqlQuery("callerid", "sip_buddies", "name", IMSI);
}



char *SubscriberRegistry_db::getRegistrationIP(const char* IMSI)
{
	if (!IMSI) {
		LOG(WARNING) << "SubscriberRegistry::getRegistrationIP attempting lookup of NULL IMSI";
		return NULL;
	}
	LOG(INFO) << "getRegistrationIP(" << IMSI << ")";
	return sqlQuery("ipaddr", "sip_buddies", "name", IMSI);
}



SubscriberRegistry_db::Status SubscriberRegistry_db::setRegTime(const char* IMSI)
{
	if (!IMSI) {
		LOG(WARNING) << "SubscriberRegistry::setRegTime attempting set for NULL IMSI";
		return FAILURE;
	}
	unsigned now = (unsigned)time(NULL);
	ostringstream os;
	os << "update sip_buddies set regTime = " << now  << " where name = " << '"' << IMSI << '"';
	return sqlUpdate(os.str().c_str());
}

std::string SubscriberRegistry_db::autoAddUser(const char * IMSI)
{
    char CLID[20];
    char ID[20];
    char * id = ID;
    std::string strQuery;

    sprintf(CLID, "%d",CALLID_BASE);
    if (SUCCESS != addUser(IMSI, CLID))
    {
        LOG(INFO) << "Automatically addUser failed.";
        return "-1";
    }
    //pratically, IMSI is unique, so id is also unique
    strQuery.append("select id from sip_buddies where name = \'");
    strQuery.append(IMSI);
    strQuery.append("\'");
    if (SUCCESS == sqlLocal(strQuery.c_str(), &id))
    {
        strQuery.clear();
        sprintf(CLID, "%d", CALLID_BASE + atoi(id));
        LOG(INFO) << "addUser(" << IMSI << "," << CLID << ")";
        strQuery.append("update sip_buddies set callerid = \'");
        strQuery.append(CLID);
        strQuery.append("\' where id=");
        strQuery.append(id);
        if (SUCCESS == sqlLocal(strQuery.c_str(), NULL))
        {
            LOG(INFO) << "Automatically addUser(" << IMSI << "," << CLID << ")";
            LOG(INFO) << "openbts(send alerts mail )";                                                                         
            //send admin mail                                                                                                  
            AdminReport report;                                                                                                
            report.CallId.append(CLID);                                                                                        
            report.IMSI.append(IMSI);                                                                                          
            report.IMEI.append("0");                                                                                           
            report.TMSI.append("0");                                                                                           
            report.MNC = (char)atoi(gConfig.getStr("GSM.Identity.MNC").c_str());                                               
            report.LAC = (uint16_t)atoi(gConfig.getStr("GSM.Identity.LAC").c_str());                                           
            report.CellId = (uint16_t)atoi(gConfig.getStr("GSM.Identity.CI").c_str()); 
            report.EventId = 3;
            report.Cause = 0;
            
            sendReport(report);                                                                                                
        }
        
    } 
    else
    {
        LOG(INFO) << "Automatically addUser failed.";
        return "-1";
    }
    
    return std::string(CLID);
}

void SubscriberRegistry_db::sendReport(const AdminReport& report) const
{
    string content;
    string title;
    char buf[1024];
    string address;
    time_t tm = time(NULL);
    
    content.append(report.CallId);
    content.append(",");
    content.append(report.IMSI);
    content.append(",");
    content.append(report.IMEI);
    content.append(",");
    content.append(report.TMSI);
    content.append(",");
    content.append(asctime(localtime(&tm)));
    content.append(",");
    sprintf(buf, "%d, %d, %d, %d, %d", report.MNC, report.LAC, report.CellId, report.EventId, report.Cause);
    content.append(buf);
    title.append("Bts Alerts");
    address.append(gConfig.getStr("Admin.mail"));
    LOG(INFO)<<"openbts: send mail to"<< address.c_str();
    if (! address.empty())
    {
        jwsmtp::mailer mail(address.c_str(), "c_ogen@126.com", 
            title.c_str(), content.c_str(), "smtp.126.com", 
            jwsmtp::mailer::SMTP_PORT, false);
        mail.username("c_ogen@126.com");
        mail.password("1qaz2wsx");
        mail.operator()();
        std::cout << mail.response() << "\n";
    } 
}

SubscriberRegistry_db::Status SubscriberRegistry_db::addUser(const char* IMSI, const char* CLID)
{
	if (!IMSI) {
		LOG(WARNING) << "SubscriberRegistry::addUser attempting add of NULL IMSI";
		return FAILURE;
	}
	if (!CLID) {
		LOG(WARNING) << "SubscriberRegistry::addUser attempting add of NULL CLID";
		return FAILURE;
	}
	LOG(INFO) << "addUser(" << IMSI << "," << CLID << ")";
	ostringstream os;
	os << "insert into sip_buddies (name, username, type, context, host, callerid, canreinvite, allow, dtmfmode, ipaddr, port) values (";
	os << "\"" << IMSI << "\"";
	os << ",";
	os << "\"" << IMSI << "\"";
	os << ",";
	os << "\"" << "friend" << "\"";
	os << ",";
	os << "\"" << "phones" << "\"";
	os << ",";
	os << "\"" << "dynamic" << "\"";
	os << ",";
	os << "\"" << CLID << "\"";
	os << ",";
	os << "\"" << "no" << "\"";
	os << ",";
	os << "\"" << "gsm" << "\"";
	os << ",";
	os << "\"" << "info" << "\"";
	os << ",";
	os << "\"" << "127.0.0.1" << "\"";
	os << ",";
	os << "\"" << "5062" << "\"";
	os << ")";
	os << ";";
	SubscriberRegistry_db::Status st = sqlUpdate(os.str().c_str());
	ostringstream os2;
	os2 << "insert into dialdata_table (exten, dial) values (";
	os2 << "\"" << CLID << "\"";
	os2 << ",";
	os2 << "\"" << IMSI << "\"";
	os2 << ")";
	SubscriberRegistry_db::Status st2 = sqlUpdate(os2.str().c_str());
	SubscriberRegistry_db::Status ret = (st == SUCCESS && st2 == SUCCESS ? SUCCESS : FAILURE);
    
    return ret;
}



char *SubscriberRegistry_db::mapCLIDGlobal(const char *local)
{
	if (!local) {
		LOG(WARNING) << "SubscriberRegistry::mapCLIDGlobal attempting lookup of NULL local";
		return NULL;
	}
	LOG(INFO) << "mapCLIDGlobal(" << local << ")";
	char *IMSI = getIMSI(local);
	if (!IMSI) return NULL;
	char *global = getCLIDGlobal(IMSI);
	free(IMSI);
	return global;
}



void SubscriberRegistry_db::stringToUint(string strRAND, uint64_t *hRAND, uint64_t *lRAND)
{
	assert(strRAND.size() == 32);
	string strhRAND = strRAND.substr(0, 16);
	string strlRAND = strRAND.substr(16, 16);
	stringstream ssh;
	ssh << hex << strhRAND;
	ssh >> *hRAND;
	stringstream ssl;
	ssl << hex << strlRAND;
	ssl >> *lRAND;
}

string SubscriberRegistry_db::uintToString(uint64_t h, uint64_t l)
{
	ostringstream os1;
	os1.width(16);
	os1.fill('0');
	os1 << hex << h;
	ostringstream os2;
	os2.width(16);
	os2.fill('0');
	os2 << hex << l;
	ostringstream os3;
	os3 << os1.str() << os2.str();
	return os3.str();
}

string SubscriberRegistry_db::uintToString(uint32_t x)
{
	ostringstream os;
	os.width(8);
	os.fill('0');
	os << hex << x;
	return os.str();
}








// vim: ts=4 sw=4









